/////////////////////////////////////////////////////////////////////
//
//  MOE Framework (Meta Object Encapsulation Framework)
//
//  Original Author: Wiksy Wang, 2011
//
/////////////////////////////////////////////////////////////////////

#ifndef MOE_NAME_NAMED_OBJECT_MANAGER_HPP
#define MOE_NAME_NAMED_OBJECT_MANAGER_HPP

#include <boost/noncopyable.hpp>
#include <boost/unordered_map.hpp>

#include "../CommonDef.h"
#include "../ObjectTypeManager.h"
#include "../RawByteVector.h"

namespace MOE
{

namespace Name
{

// Keeps record of all named objects.
// Note that the manager only handles memory allocation/deallocation of these named objects through pointer policy,
// thus if the pointer policy doesn't automatically release the memory of the named objects (e.g. through smart pointers),
// memory leak will occur.
template <template <class> class PointerPolicy>
class NamedObjectManager : boost::noncopyable
{
public:
	typedef typename PointerPolicy<void>::Type BasePointerType;

	static NamedObjectManager& GetSingleton()
	{
		static NamedObjectManager singleton;
		return singleton;
	}

	template <class T>
	typename PointerPolicy<T>::Type GetObject(const RawByteVector& objName) const
	{
		return BasePointerType::StaticCast<T>(GetObject(ObjectTypeManager::GetClassID<T>(), objName));
	}

	template <class T>
	void SetObject(const RawByteVector& objName, const typename PointerPolicy<T>::Type& object)
	{
		SetObject(objName, ObjectTypeManager::GetClassID<T>(), PointerPolicy<T>::StaticCast<void>(object));
	}

	BasePointerType GetObject(ClassIDType typeID, const RawByteVector& objName) const
	{
		ObjectMap::const_iterator it = _managedObjects.find(objName);
		if (it == _managedObjects.end())
		{
			return BasePointerType();
		}
		else
		{
			// TODO: add in support for type check.
			return it->second._pointer;
		}
	}

	void SetObject(const RawByteVector& objName, ClassIDType typeID, const BasePointerType& object)
	{
		_managedObjects[objName] = PointerMeta(typeID, object);
	}

	bool RemoveObject(const RawByteVector& objName)
	{
		ObjectMap::iterator it = _managedObjects.find(objName);
		if (it == _managedObjects.end())
		{
			return false;
		}
		else
		{
			_managedObjects.erase(it);
			return true;
		}
	}

	void RemoveAllObjects()
	{
		_managedObjects.clear();
	}

	template <class T>
	const RawByteVector& GetObjectName(const typename PointerPolicy<T>::Type& object) const
	{
		return GetObjectName(PointerPolicy<T>::StaticCast<void>(object));
	}

	template <>
	const RawByteVector& GetObjectName<void>(const BasePointerType& object) const
	{
		// TODO: This is not meant to be efficient at all.
		// The conversion method from object to object name should be templatized into a policy later.
		for (ObjectMap::const_iterator it = _managedObjects.begin(), itEnd = _managedObjects.end(); it != itEnd; ++it)
		{
			if (it->second._pointer == object)
			{
				return it->first;
			}
		}

		return RawByteVector::gEmptyStr;
	}

	template <class F>
	void Visit(F& visitor) const
	{
		for (ObjectMap::const_iterator it = _managedObjects.begin(), itEnd = _managedObjects.end(); it != itEnd; ++it)
		{
			visitor(it->first, it->second._type, it->second._pointer);
		}
	}

private:
	struct PointerMeta
	{
		PointerMeta(ClassIDType type = 0, const BasePointerType& ptr = BasePointerType())
			: _type(type)
			, _pointer(ptr)
		{
		}

		ClassIDType _type;
		BasePointerType _pointer;
	};

	typedef boost::unordered_map<RawByteVector, PointerMeta> ObjectMap;

	NamedObjectManager()
		: _managedObjects()
	{
	}

	ObjectMap _managedObjects;
};

}

}

#endif
